package com.mazaiting.instructions.control;

import com.mazaiting.instructions.base.BranchInstruction;
import com.mazaiting.instructions.base.BytecodeReader;
import com.mazaiting.rtda.MtFrame;

/**
 * switch-case语句有两种实现方式：如果case值可以编码成一个索引表，则实现成tableswitch指令
 * 否则实现成lookupswitch指令
 *
 * int chooseNear(int i) {
 * switch (i) {
 * case 0: return 0;
 * case 1: return 1;
 * case 2: return 2;
 * default: return -1;
 * }
 * }
 *
 * @author mazaiting
 * @since 2021/8/15
 */
public class TablesSwitchInstruction {

    public static class TablesSwitch extends BranchInstruction {

        /**
         * 默认情况下执行跳转所需的字节码偏移量
         * 操作码的后面有0~3字节的padding，以保证defaultOffset在字节码中的地址是4的倍数
         */
        private int defaultOffset;
        /**
         * low和high记录case的取值范围
         */
        private int low;
        /**
         * low和high记录case的取值范围
         */
        private int high;
        /**
         * jumpOffsets是一个索引表，里面存放high-low+1个int值，对应各种case情况下，执行跳转所需的字节码偏移量
         */
        private int[] jumpOffsets;

        @Override
        public void fetchOperands(BytecodeReader reader) {
            // 跳过填充字节, 保证defaultOffset在字节码中的地址是4的倍数
            reader.skipPadding();
            this.defaultOffset = reader.readU4();
            this.low = reader.readU4();
            this.high = reader.readU4();
            int jumpOffsetsCount = high - low + 1;
            this.jumpOffsets = reader.readU4s(jumpOffsetsCount);
        }

        @Override
        public void execute(MtFrame frame) {
            // 弹出索引
            int index = frame.getOperandStack().popInt();
            if (index >= low && index <= high) {
                // 跳转偏移量
                offset = jumpOffsets[index - low];
            } else {
                offset = defaultOffset;
            }
            branch(frame, index);
        }
    }
}
